#include <iostream>
#include <queue>
#include <unistd.h>
#include <pthread.h>
#define NUM 5

using namespace std;

template <class T>
class BlockQueue
{
private:
    queue<T> _q;               // 阻塞队列
    pthread_mutex_t _mutex;    // 互斥锁
    pthread_cond_t _consumers; // 消费者条件变量
    pthread_cond_t _producer;  // 生产者条件变量
    int _capacity;             // 阻塞队列容量
public:

    //初始化锁和条件变量
    BlockQueue(int capacity = NUM)
        : _capacity(capacity)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_consumers, nullptr);
        pthread_cond_init(&_producer, nullptr);
    }

    //销毁锁和条件变量
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_consumers);
        pthread_cond_destroy(&_producer);
    }

    //阻塞队列里插入数据
    void push(const T &in)
    {
        pthread_mutex_lock(&_mutex);

        //当阻塞队列为空时不能再插入变量,需要做到同步，让线程进入到生产者的条件变量中去
        while (_q.size() == _capacity) //这里一定要是while ,因为唤醒可能造成伪唤醒（如果同时唤醒条件变量中的所有线程，那么可能造成伪唤醒）
        {                              
            pthread_cond_wait(&_producer, &_mutex);
        }

        _q.push(in);

        pthread_cond_signal(&_consumers); //push数据后，阻塞队列一定有数据存在，所以唤醒消费者的条件变量。

        pthread_mutex_unlock(&_mutex);
    }

    //从阻塞队列里拿出数据
    T pop()
    {
        pthread_mutex_lock(&_mutex);
        //当阻塞队列为空时，执行该代码的线程放入到消费者条件变量中
        while (_q.size() == 0)
        {
            pthread_cond_wait(&_consumers, &_mutex);
        }

        T data = _q.front();
        _q.pop();
        
        pthread_cond_signal(&_producer); //pop数据后，阻塞队列一定是满的，唤醒生产者的条件变量
        pthread_mutex_unlock(&_mutex);

        return data;
    }

};
